package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/httphandler"
	dwr "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/dwr/v3"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/dwr/v3/model"
)

const (
	endpoint        = "endpoint"
	ak              = "access_key"
	sk              = "secret_key"
	projectId       = "projectId"
	actionAgency    = "agency"
	outputBucket    = "bucketName"
	outputPath      = "output/"
	imgUrl          = "imgUrl"
	imgDownloadPath = "result.jpg"
)

func CreateClient() *dwr.DwrClient {
	client := dwr.NewDwrClient(
		dwr.DwrClientBuilder().
			WithEndpoint(endpoint).
			WithCredential(
				basic.NewCredentialsBuilder().
					WithAk(ak).
					WithSk(sk).
					WithProjectId(projectId).
					Build()).
			WithHttpConfig(config.DefaultHttpConfig().
				WithIgnoreSSLVerification(true).
				WithHttpHandler(httphandler.NewHttpHandler().AddResponseHandler(func(response http.Response) {
					fmt.Println(response.Header)
				}))).
			Build())
	return client
}

func BuildInput(templateActions []model.ProvidedAction) []model.Input {
	cropG := "tl"
	cropH := "100"
	cropW := "100"
	cropX := "0"
	cropY := "0"
	cropOutBucket := outputBucket
	cropOutPath := outputPath
	cropSourcePath := "true"
	workflowInputs := make([]model.Input, 0)
	for j, _ := range templateActions {
		inputs := *templateActions[j].Inputs
		for _, input := range inputs {
			valueType := model.GetInputParaValueTypeEnum().STRING
			input.ValueType = &valueType
			switch input.ParameterName {
			case "crop_g":
				input.ParameterValue = &cropG
			case "crop_h":
				input.ParameterValue = &cropH
			case "crop_outbucket":
				input.ParameterValue = &cropOutBucket
			case "crop_outpath":
				input.ParameterValue = &cropOutPath
			case "crop_source_path":
				input.ParameterValue = &cropSourcePath
			case "crop_w":
				input.ParameterValue = &cropW
			case "crop_x":
				input.ParameterValue = &cropX
			case "crop_y":
				input.ParameterValue = &cropY
			default:
			}
			templateParameterName := input.ParameterName
			input.TemplateParameterName = &templateParameterName
			workflowInputs = append(workflowInputs, input)
		}
	}
	return workflowInputs
}

func BuildParam(templateActions []model.ProvidedAction) ([]model.Input, []model.Action) {
	payloadFilter := "$"
	actions := make([]model.Action, 0)
	for i, _ := range templateActions {
		templateActionCategory := templateActions[i].Category.Value()
		actions = append(actions, model.Action{
			ActionName:                 templateActions[i].TemplateName,
			ActionAgency:               actionAgency,
			Function:                   "",
			FunctionTemplate:           templateActions[i].FunctionTemplate,
			ActionTemplateName:         &templateActions[i].TemplateName,
			ActionTemplateCategory:     &templateActionCategory,
			ActionTemplateProviderName: &templateActions[i].ProviderName,
			InvocationMode:             "SYNC",
			Timeout:                    30,
			PayloadFilter:              &payloadFilter,
			DynamicSource:              templateActions[i].DynamicSourceDefinition,
		})
	}
	workflowInputs := BuildInput(templateActions)
	return workflowInputs, actions
}

func CreateWorkflow(client *dwr.DwrClient, workflowName string, workflowInputs []model.Input, actions []model.Action) error {
	payloadFilter := "$"
	supportAnonymous := true
	mode := model.GetCreateWorkflowRequestBodyModeEnum().EXPRESS
	description := "description"
	start := true
	actionMode := "SEQUENTIAL"
	endState := "End"

	createResponse, err := client.CreateWorkflow(&model.CreateWorkflowRequest{
		GraphName: workflowName,
		Body: &model.CreateWorkflowRequestBody{
			States: &[]model.State{{
				Type:      model.GetStateTypeEnum().END,
				StateName: endState,
			}, {
				Start:            &start,
				Type:             model.GetStateTypeEnum().OPERATION,
				PayloadFilterIn:  &payloadFilter,
				PayloadFilterOut: &payloadFilter,
				StateName:        "ImageCrop",
				ActionMode:       &actionMode,
				Actions:          &actions,
				NextState:        &endState,
			}},
			Inputs:      &workflowInputs,
			Description: &description,
			Mode:        &mode,
			ExpressConfig: &model.ExpressConfig{
				SupportAnonymous: &supportAnonymous,
			},
		},
	})
	if err != nil || createResponse.HttpStatusCode != http.StatusCreated {
		fmt.Printf("failed to create workflow, err: %#v, response: %#v", err, createResponse)
		return err
	}
	return nil
}

func WriteResult(body io.Reader) {
	file, err := os.Create(imgDownloadPath)
	if err != nil {
		fmt.Printf("failed to create file: %s", err.Error())
		return
	}
	defer func() {
		err := file.Close()
		if err != nil {
			fmt.Printf("failed to close file")
		}
	}()
	err = file.Chmod(0600)
	if err != nil {
		fmt.Printf("failed to chmod: %s", err.Error())
		return
	}
	_, err = io.Copy(file, body)
	if err != nil {
		fmt.Printf("failed to write image file: %s", err.Error())
		return
	}
}

// 以图片裁剪为例演示使用公开算子创建工作流
func main() {
	// 1、构造client
	client := CreateClient()
	// 2、查询系统算子
	templateInfoResponse, err := client.ShowSystemTemplateDetail(&model.ShowSystemTemplateDetailRequest{TemplateName: "ImageCrop"})
	if err != nil {
		fmt.Printf("failed to query public action template: %s", err.Error())
		return
	}
	if templateInfoResponse == nil || len(*templateInfoResponse.ProvidedActions) == 0 {
		fmt.Printf("failed to query public action template: actions is empty")
		return
	}
	// 3、创建工作流
	templateActions := *templateInfoResponse.ProvidedActions
	workflowInputs, actions := BuildParam(templateActions)
	workflowName := "workflow_create_by_sdk" + strconv.FormatInt(time.Now().Unix(), 10)
	err = CreateWorkflow(client, workflowName, workflowInputs, actions)
	if err != nil {
		fmt.Printf("failed to query public action template: %s", err.Error())
		return
	}
	// 4、触发工作流
	response, err := http.Get(fmt.Sprintf("%s?x-workflow-graph-name=%s", imgUrl, workflowName))
	if err != nil {
		fmt.Printf("failed to get cropped image: %s", err.Error())
		return
	}
	defer func() {
		err := response.Body.Close()
		if err != nil {
			fmt.Printf("failed to close body\n")
		}
	}()
	WriteResult(response.Body)
}
